/**
 * Copyright (c) 2008 - TheNittyGritty - Thomas Brekelmans
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package nl.thenittygritty.command 
{
	import flash.events.ErrorEvent;
	import flash.events.Event;

	import nl.thenittygritty.command.AbstractCommand;
	import nl.thenittygritty.utils.ArrayUtils;		

	/**
	 * CommandBatch provides addCommand and removeCommand methods which add or 
	 * remove any given command from the batch. CommandBatch extends 
	 * AbstractCommand and executes all its stored commands at once as soon as 
	 * its execute method is called. 
	 * Each command is added to the given command history (if defined) as soon 
	 * as it is succesfully completed. 
	 * If all commands in the batch are completed this command's Event.COMPLETE 
	 * event is sent. 
	 * If an error occursin any command in the batch this commands's 
	 * ErrorEvent.ERROR is immediatly sent. 
	 * Note: if you are batching multiple asynchronous commands one command 
	 * might encounter an error and cause the ErrorEvent.ERROR for the whole 
	 * batch to trigger before all commands have been completed.
	 */
	public class CommandBatch extends AbstractCommand 
	{
		private var commands:Array;
		private var commandsCompleted:uint;
		
		private var commandHistory:CommandHistory;

		/**
		 * Creates a new CommandBatch.
		 */
		public function CommandBatch(receiver:ICommandReceiver, 
									 commandHistory:CommandHistory = null)
		{
			super(receiver);
			
			commands = new Array();
			commandsCompleted = 0;
			
			this.commandHistory = commandHistory;
		}

		/**
		 * Executes each command in the batch at once.
		 */
		override public function execute():void
		{
			for (var i:uint; i < commands.length; ++i)
			{
				var currentCommand:AbstractCommand = commands[i];
				currentCommand.addEventListener(Event.COMPLETE, 
												onCurrentCommandComplete);
				currentCommand.addEventListener(ErrorEvent.ERROR, 
												onCurrentCommandError);
				currentCommand.execute();
			}
		}

		/**
		 * Adds a given command to the batch.
		 */
		public function addCommand(command:ICommand):void
		{
			commands.push(command);
		}

		/**
		 * Removes a given command from the batch if it exists in it.
		 */
		public function removeCommand(command:ICommand):void
		{
			var commandIndex:int = ArrayUtils.getItemIndex(command, commands);
			if (commandIndex != -1)
			{
				commands.splice(commandIndex, 1);
			}
		}
		
		/**
		 * Handles the current command complete event and checks if all commands 
		 * have been completed.
		 */
		private function onCurrentCommandComplete(e:Event):void
		{
			commandsCompleted++;
			
			if (commandsCompleted == commands.length)
			{
				onCommandComplete();	
			}
		}
		
		/**
		 * Handles the current command error event and reports an error for 
		 * the command batch.
		 */
		private function onCurrentCommandError(e:ErrorEvent):void
		{
			onCommandError(e.text);
		}
		
		/**
		 * Returns the name of the class in a String.
		 */
		public function toString():String 
		{
			return "CommandBatch";
		}
	}
}
